#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <fastcgi++/request.hpp>
#include <fastcgi++/manager.hpp>
#include "graph.h"
#include "graph_algorithms.h"

void log_error(char const * msg)
{
  static std::ofstream s("logfile.txt");
  s << msg << std::endl;
}

namespace details
{
  struct output_writer_t
  {
    output_writer_t(db_graph_t const * g) :
      first_write_(true), graph_(g)
    {
      out_ << "[";
    }

    output_writer_t & operator *()
    {
      return *this;
    }

    output_writer_t & operator =(vertex_id x)
    {
      geodata_t data = graph_->get_data(x);
      if (!first_write_)
      {
        out_ << ",";
      }
      else
      {
        first_write_ = false;
      }
      out_ << "[" << data.lat << ", " << data.lon << "]";
    }

    output_writer_t & operator ++()
    {
      return *this;
    }

    output_writer_t & operator ++(int)
    {
      return *this;
    }

    std::string text() const
    {
      return out_.str() + "]";
    }
  private:
    bool first_write_;
    std::ostringstream out_;
    db_graph_t const * graph_;
  };


  db_graph_t & get_graph(std::string const & db_init = std::string(""))
  {
    static db_graph_t graph(db_init);
    log_error("Returned graph");
    return graph;
  }
}

struct ShortestPathRequest: Fastcgipp::Request<char>
{
  bool response()
  {
    Gets const & gets = environment().gets;
    geodata_t pt_src(from_gets<float> (gets, "lat1"),
                     from_gets<float> (gets, "lng1"));
    geodata_t pt_dst(from_gets<float> (gets, "lat2"),
                     from_gets<float> (gets, "lng2"));
    out << "Content-Type: application/json;\r\n\r\n";

    db_graph_t & g = details::get_graph();
//    vertex_id v1 = g.add_vertex(pt_src);
//    vertex_id v2 = g.add_vertex(pt_dst);

//    write_temp_edges(g, v1, v2);
//
    details::output_writer_t writer(&g);
//    dijkstra_shortest_path<details::output_writer_t &>(g, v1, v2, writer);

    out << writer.text();
    return true;
  }
private:
  typedef Fastcgipp::Http::Environment<char>::Gets Gets;

//  void write_temp_edges(db_graph_t & g, vertex_id v1, vertex_id v2)
//  {
//    geodata_t v1_data = g.get_data(v1);
//    geodata_t v2_data = g.get_data(v2);
//
//    pqxx::connection & conn = details::get_connection();
//    pqxx::work t(conn, "EDGES");
//
//    pqxx::result edges_1 = t.exec("SELECT osm_id, ST_DISTANCE(geopoint::geography, "
//        "'SRID=4326;POINT(" + boost::lexical_cast<std::string>(v1_data.lat) + " " + boost::lexical_cast<std::string>(v1_data.lng) +")') as dist "
//        "FROM points "
//        "WHERE ST_DWITHIN(geopoint::geography, 'SRID=4326;POINT(" + boost::lexical_cast<std::string>(v1_data.lat) + " " + boost::lexical_cast<std::string>(v1_data.lng) +")', 4000) "
//        "ORDER BY dist "
//        "LIMIT 4");
//
//    for (pqxx::result::const_iterator it = edges_1.begin(); it != edges_1.end(); ++it)
//    {
//      //g.add_edge(v1, (*it)[0].as<vertex_id>(), (*it)[1].as<float>());
//    }
//
//    pqxx::result edges_2 = t.exec("SELECT osm_id, ST_DISTANCE(geopoint::geography, "
//        "'SRID=4326;POINT(" + boost::lexical_cast<std::string>(v2_data.lat) + " " + boost::lexical_cast<std::string>(v2_data.lng) +")') as dist "
//        "FROM points "
//        "WHERE ST_DWITHIN(geopoint::geography, 'SRID=4326;POINT(" + boost::lexical_cast<std::string>(v2_data.lat) + " " + boost::lexical_cast<std::string>(v2_data.lng) +")', 4000) "
//        "ORDER BY dist "
//        "LIMIT 4");
//    for (pqxx::result::const_iterator it = edges_2.begin(); it != edges_2.end(); ++it)
//    {
//      //g.add_edge((*it)[0].as<vertex_id>(), v2, (*it)[1].as<float>());
//    }
//  }

  template<class T>
  static T from_gets(Gets const & gets, std::string const & name,
                     T const & def = T())
  {
    Gets::const_iterator it = gets.find(name);
    if (it == gets.end())
    {
      return def;
    }
    else
    {
      return boost::lexical_cast<T>(it->second);
    }
  }
};

int main(int argc, char * argv[])
{
  log_error("Main started.");
  try
  {
    Fastcgipp::Manager<ShortestPathRequest> manager;
    manager.handler();
  }
  catch (std::exception & e)
  {
    log_error(e.what());
  }

  log_error("Main done.");
}
